package com.su.utils;

import com.su.utils.ByteUtils;
import com.su.utils.CRC16;
import lombok.Getter;
import lombok.Setter;

public class ModBus {
    /** Application Data Unit*/
    public static class ADU {
        @Getter
        @Setter
        private String address; // 1 bytes
        /** Protocol Data Unit */
        @Setter@Getter
        public static class PDU {
            /** valid codes are in the range of [1, 255] decimal */
            public enum FunctionCodes {
                READ_HOLDING_REGISTERS(0x03),
                WRITE_SINGLE_REGISTER(0x06),
                WRITE_MULTIPLE_REGISTERS(0x10);
                // [128, 255] is reserved for Exception Function Codes
                public static boolean isException(int code){
                    return code >= 128 && code <= 255;
                }

                @Override
                public String toString() {
                    return String.valueOf(hex);
                }

                int hex;

                FunctionCodes(int hex) {
                    this.hex = hex;
                }

                public static class ModBusFunctionCodeException extends RuntimeException {
                    public ModBusFunctionCodeException() {
                        super();
                    }

                    public ModBusFunctionCodeException(String message) {
                        super(message);
                    }
                }
            }

            private String funCode;
            private String data;

            public PDU() {
            }
            public PDU(String hexString) {
                this.setFunCode(hexString.substring(0, 2));
                this.setData(hexString.substring(2));
            }

            @Override
            public String toString() {
                return funCode + data;
            }
        }
        @Getter@Setter private PDU pdu;
        /** Error Check(CRC(Cyclic Redundancy Check)) 2 bytes */
        private String errorCheck(){
            // 根据address+funCode+data，计算CRC16
            String s = (address + pdu);
            String dataHEX = s.replace(" ","");
            byte[] data = ByteUtils.hexStringToBytes(dataHEX);
            int crcInt = CRC16.calcCrc16(data);
            String crcStr = String.format("0x%04x", crcInt);
            String hi = crcStr.substring(2,4);
            String lo = crcStr.substring(4,6);
            // 逆序
            return lo + hi;
        }

        public ADU() {}
        public ADU(String address, PDU pdu) {
            this.address = address;
            this.pdu = pdu;
        }
        @Override
        public String toString() {
            return (address + pdu + errorCheck()).replace(" ","");
        }
    }

}
